// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-07-25

package logics

import (
	"context"
	"gitee.com/go-libs/log"
	"gitee.com/go-libs/result"
	"gitee.com/go-wares/framework-iris/framework/src/errs"
	"gitee.com/go-wares/framework-iris/framework/src/middlewares"
	"github.com/kataras/iris/v12"
	"net/http"
)

// Do
// a logical process.
func Do(i iris.Context, logic Logic) {
	var (
		ctx                context.Context
		logical, name, err = Class.Build(i, logic)
		res                result.Result
	)

	// Context
	// inherit or build for open tracing, open telemetry.
	if span, ok := i.Values().Get(middlewares.TracingSpan).(log.Span); ok {
		span.GetAttr().Set("logical", name)
		ctx = span.GetContext()
	} else {
		ctx = i.Request().Context()
	}

	// Logger
	// begin logical.
	log.Infofc(ctx, `logical begin: logic="%s"`, name)

	// Call
	// when logical process end.
	defer func() {
		// Catch
		// runtime panic and override result.
		if r := recover(); r != nil {
			res = result.New().WithError(errs.LogicPanic)
			log.Fatalfc(ctx, `logical panic: logic="%s", fatal="%v"`, name, r)
		}

		// Send
		// response result to context and stop it.
		var es error

		i.StatusCode(http.StatusOK)

		if res.IsHtml() {
			_, es = i.HTML(res.String())
		} else if res.IsString() {
			_, es = i.Text(res.String())
		} else {
			es = i.JSON(res)
		}
		if es != nil {
			log.Errorfc(ctx, `logical error: logic="%s", error="%v"`, name, es)
		}

		// Put
		// logical instance to pool if implemented.
		if v, ok := logical.(LogicalReleaser); ok {
			v.Release()
		}

		// Logger
		// end logical.
		log.Infofc(ctx, `logical finish: response=%s`, res)
	}()

	// Check
	// pre to do processor.
	if err != nil {
		res = result.New().WithError(err)
		return
	}

	// Catch
	// runtime panic.
	defer func() {
		// Reset error result
		// if not returned in runner hook.
		if res == nil {
			res = result.New().WithError(errs.ResultNotReturned)
		}

		// Hook
		// for after.
		if v, ok := logical.(LogicalAfter); ok {
			v.After(ctx, i, res)
		}
	}()

	// Hook
	// for before if implemented.
	if v, ok := logical.(LogicalBefore); ok {
		if err = v.Before(ctx, i); err != nil {
			res = result.New().WithError(err)
			return
		}
	}

	// Hook
	// for runner.
	res = logical.Run(ctx, i)
}
